The following notebook is an exercise for the Convolutional Neural Networks for Computer Vision course at Afeka College of Engineering.
It uses Kaggle's Face Mask Detection dataset for Multi-Label Object Detection.
Table of Contents:
Submitted By:
import os
import sys
from datetime import datetime
import numpy as np
import pandas as pd
import cv2
import torch
from torch.utils.data import DataLoader
import torch.optim as optim
import torchvision
from sklearn import metrics
from iterstrat.ml_stratifiers import MultilabelStratifiedKFold
from matplotlib import pyplot as plt
import seaborn as sns
from data_handler.FaceMaskData import FaceMaskData
from data_handler.FaceMaskDataset import FaceMaskDataset
from trainer import *
from metrics.metrics import *
from criterion.criterion import *
assert torch.cuda.is_available()
import json
class CFG:
seed = 42
model_name = 'faster_rcnn'
pretrained = True
img_width = 480
img_height = 480
batch_size = 10
n_epochs = 10
n_folds = 5
drop_rate = 0.
train_size = 0.90
nms_thresh = 0.2
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
num_workers = 4
num_classes = 4
imgs_path = 'images' # images dir
msks_path = 'annotations' # annotations dir
multilabelKFold = True
optimizer = optim.SGD
optimizer_dict = { 'lr': 0.005,
'momentum': 0.9,
'weight_decay': 0.0005 }
scheduler = optim.lr_scheduler.StepLR
scheduler_dict = { 'step_size': 5,
'gamma': 0.1 }
def save(path):
save_path = path + '/model_dict.json'
with open(save_path, 'w') as f:
for key, val in CFG.__dict__.items():
f.write('{}\t\t= {}\n'.format(key, val))
# optimizer = optim.Adam
# optimizer_dict = None
# scheduler = optim.lr_scheduler.StepLR
# scheduler_dict = { 'step_size': 5,
# 'gamma': 0.1 }
# optimizer = optim.ASGD
# optimizer_dict = None
# scheduler = optim.lr_scheduler.ExponentialLR
# scheduler_dict = { gamma: 0.9 }
np.random.seed(CFG.seed)
Load the data into a dataset
df = pd.read_csv(os.path.join(sys.path[0], 'annotation.csv'))
if CFG.multilabelKFold and CFG.n_folds > 1:
faceMasksData = FaceMaskData(CFG.imgs_path, CFG.msks_path, multilabelKFold=True, df_file=df)
(x_train, y_train, l_train), (x_test, y_test, l_test) = faceMasksData.load_data(
train_size=CFG.train_size,
drop_rate=CFG.drop_rate,
seed=CFG.seed)
else:
faceMasksData = FaceMaskData(CFG.imgs_path, CFG.msks_path)
(x_train, y_train), (x_test, y_test) = faceMasksData.load_data(
train_size=CFG.train_size,
drop_rate=CFG.drop_rate,
seed=CFG.seed)
print('Training contains {} samples which is {:g}% of the data'.format(len(x_train), len(x_train) * 100 / (len(x_train) + len(x_test))))
print('Testing contains {} samples which is {:g}% of the data'.format(len(x_test), len(x_test) * 100 / (len(x_train) + len(x_test))))
Training contains 767 samples which is 89.9179% of the data Testing contains 86 samples which is 10.0821% of the data
testset = FaceMaskDataset(x_test, y_test, CFG.imgs_path, CFG.msks_path, CFG.img_width, CFG.img_height, transforms=get_transformer('test'))
test_loader = DataLoader(dataset=testset, batch_size=CFG.batch_size, shuffle=False, num_workers=CFG.num_workers, collate_fn=collate_fn)
df.head()
| xmin | ymin | xmax | ymax | name | file | width | height | class | Xcent | Ycent | boxW | boxH | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 28 | 55 | 46 | 71 | with_mask | maksssksksss737 | 400 | 226 | 0 | 0.09250 | 0.278761 | 0.0450 | 0.070796 |
| 1 | 98 | 62 | 111 | 78 | with_mask | maksssksksss737 | 400 | 226 | 0 | 0.26125 | 0.309735 | 0.0325 | 0.070796 |
| 2 | 159 | 50 | 193 | 90 | mask_weared_incorrect | maksssksksss737 | 400 | 226 | 1 | 0.44000 | 0.309735 | 0.0850 | 0.176991 |
| 3 | 293 | 59 | 313 | 80 | with_mask | maksssksksss737 | 400 | 226 | 0 | 0.75750 | 0.307522 | 0.0500 | 0.092920 |
| 4 | 352 | 51 | 372 | 72 | with_mask | maksssksksss737 | 400 | 226 | 0 | 0.90500 | 0.272124 | 0.0500 | 0.092920 |
faces = len(df['file'].unique())
annotates = len(df)
print('There are total {} images in the data'.format(faces))
print('There are total {} annotated faces in the data'.format(annotates))
print('Average of {:.5f} per image'.format(annotates/faces))
There are total 853 images in the data There are total 4072 annotated faces in the data Average of 4.77374 per image
label_df = pd.DataFrame(columns=['label', 'count'])
for k, v in df['name'].value_counts().to_dict().items():
label_df = label_df.append({'label':k, 'count':v}, ignore_index=True)
display(label_df)
fig, ax = plt.subplots()
ax.bar(label_df['label'], label_df['count'], width=0.4)
plt.title('Label Count')
for index, data in enumerate(label_df['count']):
plt.text(x=index-0.1, y=data+1, s=data , fontdict=dict(fontsize=20))
plt.tight_layout()
plt.show()
| label | count | |
|---|---|---|
| 0 | with_mask | 3232 |
| 1 | without_mask | 717 |
| 2 | mask_weared_incorrect | 123 |
def get_class_distribute(df):
classes = ["with_mask","without_mask","mask_weared_incorrect"]
classes_bit_shifter_amount = {
"with_mask":0,
"without_mask":1,
"mask_weared_incorrect":2,
}
dists = np.zeros(8)
for name in faceMasksData.images:
img_name = name.split('.')[0]
img_classes = df[df['file']==img_name]['name'].unique()
index = 0
for cls in img_classes:
index |= 1 << classes_bit_shifter_amount[cls]
dists[index] += 1
if dists[0] == 0:
return dists[1:]
return dists
dist_names = ['None', 'with_mask', 'without_mask', 'with_mask\nwithout_mask',
'mask_weared_incorrect', 'with_mask\nmask_weared_incorrect',
'without_mask\nmask_weared_incorrect',
'with_mask\nwithout_mask\nmask_weared_incorrect']
dist_values = get_class_distribute(df)
if len(dist_values) == 7:
dist_names = dist_names[1:]
plt.figure(figsize=(15, 5))
axs = plt.bar(dist_names, dist_values)
for ax in axs:
plt.text(ax.get_xy()[0] + 0.3, ax.get_height(), int(ax.get_height()), fontsize='x-large')
plt.xticks(rotation=30, ha='right')
plt.title('Classes per Image')
plt.show()
Clearly, the data in these charts show a very imbalanced trend.
Only 123 faces have an incorrectly worn mask, appearing in (21 + 30 + 4 + 42) = 97 images, which is (97 / 853) = 11% of all the images.
However, the masks appeared on 768 / 853 = 90% of all images.
In later stages of training, it will be necessary to divide the train and validation sets evenly to preserve the different labels at the same percentage.
def get_num_faces(df, image_name_col):
max_faces = find_max_faces(df,image_name_col)
arr = [0] * (max_faces + 1)
faces_count =1
current_img =df.at[0,image_name_col]
for i in range(1,len(df[1:])):
if current_img == df.at[i,image_name_col]:
faces_count = faces_count + 1
else:
arr[faces_count] = arr[faces_count] +1
faces_count =1
current_img =df.at[i,image_name_col]
df = pd.DataFrame(columns=['faces', 'count'])
for i, val in enumerate(arr):
if val > 0:
df = df.append({'faces': i, 'count': val}, ignore_index=True)
return df
def find_max_faces(df,image_name_col):
max_faces=1
faces_count =1
current_img =df.at[0,image_name_col]
for i in range(1,len(df[1:])):
if current_img == df.at[i,image_name_col]:
faces_count = faces_count +1
else:
if faces_count > max_faces:
max_faces = faces_count
current_img = df.at[i,image_name_col]
faces_count =1
return max_faces
face_dist_df = get_num_faces(df, 'file')
ax = face_dist_df.plot.bar(x='faces', y='count', figsize=(15,5), title='Faces per Image')
for idx, label in enumerate(list(face_dist_df.faces)):
val = int(face_dist_df[face_dist_df['faces']==label]['count'])
ax.annotate(val,
(idx-0.2, val),
xytext=(0, 15),
textcoords='offset points')
print('STD of faces in the images: {:.5}'.format(face_dist_df['count'].std()))
STD of faces in the images: 58.229
The data is very imbalanced, as it has been shown above.
The number of appearances of each class varies greatly, as well as the amount of faces per image.
We would like to use Cross-Validation for better training.
Due to our data being imbalanced, using standard splitting methods can cause some classes to not appear at all in some folds; Moreover, our task involves Multi-label Object Detection, for which the well-known splitting algorithm cannot be used.
Stratified KFold splits the folds by preserving the percentages of samples for each label.
Scikit-learn's algorithm cannot be applied to the second problem, so we will utilize the MultilabelStratifiedKFold implementation, which can be found here.
# self.classes = [None, 'without_mask','with_mask','mask_weared_incorrect']
# MultilabelStratifiedKFold needs to get the Y as binary for each class
x_names = [ name.split('.')[0] for name in x_train ]
y_classes = []
for name in x_names:
classes = list(df[df['file']==name]['name'].unique())
indice = np.zeros(4, dtype=np.uint8)
for c in classes:
index = testset.classes.index(c)
indice[index] = 1
y_classes.append(list(indice))
print(y_classes[:5])
[[0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0]]
df_folds = pd.DataFrame()
mskf = MultilabelStratifiedKFold(CFG.n_folds)
for fold_id, (train_idx, test_idx) in enumerate(mskf.split(x_names, y_classes), start=1):
classes = np.zeros(len(testset.classes))
for idx in train_idx:
name = x_names[idx]
for cl, val in df[df['file']==name]['name'].value_counts().to_dict().items():
class_index = testset.classes.index(cl)
classes[class_index] += val
for cl, val in df['name'].value_counts().to_dict().items():
class_index = testset.classes.index(cl)
df_folds = df_folds.append({ 'fold': int(fold_id),
'class':cl,
'count': classes[class_index] ,
}, ignore_index=True)
print('fold {}: train_size={}, test_size={}'.format(fold_id, len(train_idx), len(test_idx)))
# df_folds = df_folds.append({ 'fold': fold_id,
# 'train': len(train_idx),
# 'valid': len(test_idx),
# 'without_mask': classes[testset.classes.index('without_mask')],
# 'with_mask': classes[testset.classes.index('with_mask')],
# 'mask_weared_incorrect': classes[testset.classes.index('mask_weared_incorrect')]
# }, ignore_index=True)
display(df_folds)
g = ax = sns.catplot(x='fold', y='count', hue='class', data=df_folds, kind='bar', legend=True)
g.fig.set_size_inches(15, 8)
g.fig.subplots_adjust(top=0.81, right=0.86)
# extract the matplotlib axes_subplot objects from the FacetGrid
ax = g.facet_axis(0, 0)
# iterate through the axes containers
for c in ax.containers:
labels = [int(v.get_height()) for v in c]
ax.bar_label(c, labels=labels, label_type='edge')
fold 1: train_size=616, test_size=151 fold 2: train_size=613, test_size=154 fold 3: train_size=615, test_size=152 fold 4: train_size=612, test_size=155 fold 5: train_size=612, test_size=155
| fold | class | count | |
|---|---|---|---|
| 0 | 1.0 | with_mask | 2348.0 |
| 1 | 1.0 | without_mask | 567.0 |
| 2 | 1.0 | mask_weared_incorrect | 92.0 |
| 3 | 2.0 | with_mask | 2255.0 |
| 4 | 2.0 | without_mask | 510.0 |
| 5 | 2.0 | mask_weared_incorrect | 90.0 |
| 6 | 3.0 | with_mask | 2383.0 |
| 7 | 3.0 | without_mask | 483.0 |
| 8 | 3.0 | mask_weared_incorrect | 87.0 |
| 9 | 4.0 | with_mask | 2369.0 |
| 10 | 4.0 | without_mask | 551.0 |
| 11 | 4.0 | mask_weared_incorrect | 89.0 |
| 12 | 5.0 | with_mask | 2205.0 |
| 13 | 5.0 | without_mask | 557.0 |
| 14 | 5.0 | mask_weared_incorrect | 90.0 |
From the chart above, it can be seen that MultilabelStratifiedKFold split the images into almost identical train-test sizes in each fold, while preserving the number of labels from each class within each fold.
def norm(img):
img = np.array(img, dtype=np.float32)
img -= img.min()
img /= img.max()
return img
def get_annotated_img(img, annt, is_pred=False):
img = norm(np.array(np.transpose(img, (1, 2, 0)))) * 255.0 # multiple by 255 as in the dataset we divide it
# needed here because the image come from the dataset in values of [0, 1]
# and the annotations are in values of [0, 255] and plt should get images
# with values of [0, 1], therefore a normalizing of the images is needed
num_faces = len(annt['boxes'])
for i in range(num_faces):
box, cat = annt['boxes'][i], annt['labels'][i]
if is_pred:
score = annt['scores'][i]
xmin, ymin, xmax, ymax = np.array(box, dtype=np.int32)
start_point = (xmin, ymin)
end_point = (xmax, ymax)
# [None, 'without_mask','with_mask','mask_weared_incorrect']
color = (0, 0, 0)
if cat == 2:
color = (0, 255, 0) # green
elif cat == 3:
color = (0, 0, 255) # blue
elif cat == 1:
color = (255, 0, 0) # red
thickness = 1
img = cv2.rectangle(img, start_point, end_point, color, thickness)
font = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 1 / 3
if not is_pred:
img = cv2.putText(img, '{}'.format(faceMasksData.classes[cat]), start_point, font, fontScale, color = (0, 0, 0), thickness=2)
img = cv2.putText(img, '{}'.format(faceMasksData.classes[cat]), start_point, font, fontScale, color, thickness)
else:
img = cv2.putText(img, '{:.2f}'.format(score), start_point, font, fontScale, color = (0, 0, 0), thickness=2)
img = cv2.putText(img, '{:.2f}'.format(score), start_point, font, fontScale, color, thickness)
return img
def show_augmented_samples(dataloader, model=None):
samples, annotations = next(iter(dataloader)) # get the first batch
amount_samples = min(12, len(samples)) # number of examples will be at most 12
for i, (img, ant) in enumerate(zip(samples, annotations)):
if i >= amount_samples:
break
plt.figure(figsize=(10, 10))
# plt.subplot(amount_samples, 1, i + 1)
img = get_annotated_img(img, ant)
plt.imshow(norm(img))
plt.axis('off')
plt.tight_layout()
plt.show()
show_augmented_samples(test_loader)
testset = FaceMaskDataset(x_test, y_test, CFG.imgs_path, CFG.msks_path, CFG.img_width, CFG.img_height, transforms=get_transformer('train'))
test_loader = DataLoader(dataset=testset, batch_size=CFG.batch_size, shuffle=False, num_workers=CFG.num_workers, collate_fn=collate_fn)
show_augmented_samples(test_loader)
def get_model(num_classes, pretrained=True):
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=pretrained) # get model
in_features = model.roi_heads.box_predictor.cls_score.in_features # get input size of last layer
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes) # regenerate the last layer
return model
model = get_model(num_classes=CFG.num_classes, pretrained=CFG.pretrained)
# model.roi_heads.fastrcnn_loss = custom_fastrcnn_loss
# torchvision.models.detection.roi_heads.fastrcnn_loss = custom_fastrcnn_loss
datetime_srt = datetime.today().strftime("%d-%m-%y_%H:%M")
run_path = os.path.join(sys.path[0], 'runs', datetime_srt)
if CFG.multilabelKFold and CFG.n_folds > 1:
train(model, x_train, (y_train, l_train), run_path, CFG)
else:
train(model, x_train, y_train, run_path, CFG)
This running path is: `/home/linuxgpu/anaconda3/envs/lab1/Face-Mask-Detection/runs/06-11-21_17:42`
Fold 1 of 5
Epoch 1 of 10:
train: 0%| | 0/62 [00:00<?, ?it/s]/home/linuxgpu/anaconda3/envs/lab1/lib/python3.9/site-packages/torch/functional.py:445: UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at /opt/conda/conda-bld/pytorch_1634272204863/work/aten/src/ATen/native/TensorShape.cpp:2157.) return _VF.meshgrid(tensors, **kwargs) # type: ignore[attr-defined] train: 100%|██████████| 62/62 [01:35<00:00, 1.54s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.37it/s]
train: loss_classifier=15.01781 loss_box_reg=15.05909 loss_objectness=7.53403 loss_rpn_box_reg=1.84984 total loss=39.46076 valid: loss_classifier=2.42270 loss_box_reg=3.68156 loss_objectness=0.47466 loss_rpn_box_reg=0.28470 total loss=6.86362
Model saved. Loss < PrevLoss (6.86362 < inf) Epoch 2 of 10:
train: 100%|██████████| 62/62 [01:33<00:00, 1.52s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.39it/s]
train: loss_classifier=11.00053 loss_box_reg=15.20138 loss_objectness=3.25007 loss_rpn_box_reg=1.53625 total loss=30.98823 valid: loss_classifier=1.98521 loss_box_reg=3.28301 loss_objectness=0.44045 loss_rpn_box_reg=0.23018 total loss=5.93885
Model saved. Loss < PrevLoss (5.93885 < 6.86362) Epoch 3 of 10:
train: 100%|██████████| 62/62 [01:31<00:00, 1.47s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.44it/s]
train: loss_classifier=9.90379 loss_box_reg=14.33533 loss_objectness=2.43078 loss_rpn_box_reg=1.23865 total loss=27.90855 valid: loss_classifier=1.70063 loss_box_reg=3.77738 loss_objectness=0.38289 loss_rpn_box_reg=0.24839 total loss=6.10930
Epoch 4 of 10:
train: 100%|██████████| 62/62 [01:27<00:00, 1.42s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.45it/s]
train: loss_classifier=8.66681 loss_box_reg=13.44955 loss_objectness=1.86838 loss_rpn_box_reg=1.07198 total loss=25.05672 valid: loss_classifier=1.68127 loss_box_reg=2.86243 loss_objectness=0.37210 loss_rpn_box_reg=0.22163 total loss=5.13742
Model saved. Loss < PrevLoss (5.13742 < 5.93885) Epoch 5 of 10:
train: 100%|██████████| 62/62 [01:27<00:00, 1.42s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.46it/s]
train: loss_classifier=8.49094 loss_box_reg=12.75405 loss_objectness=1.94431 loss_rpn_box_reg=1.06761 total loss=24.25691 valid: loss_classifier=1.60836 loss_box_reg=2.88824 loss_objectness=0.27162 loss_rpn_box_reg=0.20701 total loss=4.97523
Model saved. Loss < PrevLoss (4.97523 < 5.13742) Epoch 6 of 10:
train: 100%|██████████| 62/62 [01:27<00:00, 1.41s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.45it/s]
train: loss_classifier=7.91624 loss_box_reg=12.12323 loss_objectness=2.04317 loss_rpn_box_reg=1.13655 total loss=23.21919 valid: loss_classifier=1.47295 loss_box_reg=2.54276 loss_objectness=0.25826 loss_rpn_box_reg=0.18811 total loss=4.46208
Model saved. Loss < PrevLoss (4.46208 < 4.97523) Epoch 7 of 10:
train: 100%|██████████| 62/62 [01:28<00:00, 1.42s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.45it/s]
train: loss_classifier=7.87462 loss_box_reg=12.15443 loss_objectness=1.63938 loss_rpn_box_reg=0.97433 total loss=22.64277 valid: loss_classifier=1.49186 loss_box_reg=2.58215 loss_objectness=0.27125 loss_rpn_box_reg=0.19033 total loss=4.53559
Epoch 8 of 10:
train: 100%|██████████| 62/62 [01:26<00:00, 1.40s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.41it/s]
train: loss_classifier=7.71608 loss_box_reg=12.14628 loss_objectness=1.26908 loss_rpn_box_reg=0.92189 total loss=22.05332 valid: loss_classifier=1.51166 loss_box_reg=2.56411 loss_objectness=0.28309 loss_rpn_box_reg=0.18546 total loss=4.54432
Epoch 9 of 10:
train: 100%|██████████| 62/62 [01:26<00:00, 1.39s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.52it/s]
train: loss_classifier=7.53827 loss_box_reg=12.08613 loss_objectness=1.28133 loss_rpn_box_reg=0.86223 total loss=21.76795 valid: loss_classifier=1.47390 loss_box_reg=2.51616 loss_objectness=0.25913 loss_rpn_box_reg=0.18160 total loss=4.43079
Model saved. Loss < PrevLoss (4.43079 < 4.46208) Epoch 10 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.50it/s]
train: loss_classifier=7.62333 loss_box_reg=12.13703 loss_objectness=1.77611 loss_rpn_box_reg=1.36624 total loss=22.90271 valid: loss_classifier=1.44570 loss_box_reg=2.57376 loss_objectness=0.28974 loss_rpn_box_reg=0.19345 total loss=4.50265
Fold 2 of 5
Epoch 1 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.37s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=7.59083 loss_box_reg=12.15347 loss_objectness=1.49983 loss_rpn_box_reg=1.08513 total loss=22.32926 valid: loss_classifier=1.62195 loss_box_reg=3.18609 loss_objectness=0.22341 loss_rpn_box_reg=0.26321 total loss=5.29466
Epoch 2 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.48it/s]
train: loss_classifier=7.76088 loss_box_reg=11.83152 loss_objectness=1.23193 loss_rpn_box_reg=0.82651 total loss=21.65083 valid: loss_classifier=1.60944 loss_box_reg=2.87869 loss_objectness=0.25499 loss_rpn_box_reg=0.23842 total loss=4.98154
Epoch 3 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.50it/s]
train: loss_classifier=7.46766 loss_box_reg=11.72519 loss_objectness=1.50169 loss_rpn_box_reg=0.93008 total loss=21.62461 valid: loss_classifier=1.58009 loss_box_reg=2.90905 loss_objectness=0.22523 loss_rpn_box_reg=0.23275 total loss=4.94713
Epoch 4 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.37s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.35it/s]
train: loss_classifier=7.44881 loss_box_reg=11.54003 loss_objectness=1.30349 loss_rpn_box_reg=0.91087 total loss=21.20319 valid: loss_classifier=1.50507 loss_box_reg=2.81895 loss_objectness=0.26033 loss_rpn_box_reg=0.23098 total loss=4.81533
Epoch 5 of 10:
train: 100%|██████████| 62/62 [01:26<00:00, 1.40s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.47it/s]
train: loss_classifier=7.20498 loss_box_reg=11.15779 loss_objectness=1.33045 loss_rpn_box_reg=0.76849 total loss=20.46170 valid: loss_classifier=1.52462 loss_box_reg=2.92053 loss_objectness=0.22071 loss_rpn_box_reg=0.23549 total loss=4.90136
Epoch 6 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.49it/s]
train: loss_classifier=6.48085 loss_box_reg=10.69869 loss_objectness=1.41827 loss_rpn_box_reg=0.97755 total loss=19.57537 valid: loss_classifier=1.43590 loss_box_reg=2.63916 loss_objectness=0.21315 loss_rpn_box_reg=0.22348 total loss=4.51168
Epoch 7 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.54it/s]
train: loss_classifier=6.35496 loss_box_reg=10.58861 loss_objectness=1.19638 loss_rpn_box_reg=0.94232 total loss=19.08227 valid: loss_classifier=1.42330 loss_box_reg=2.67395 loss_objectness=0.22321 loss_rpn_box_reg=0.22365 total loss=4.54411
Epoch 8 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.38s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=6.22529 loss_box_reg=10.37638 loss_objectness=0.99517 loss_rpn_box_reg=0.71531 total loss=18.31215 valid: loss_classifier=1.45877 loss_box_reg=2.67073 loss_objectness=0.21830 loss_rpn_box_reg=0.22274 total loss=4.57055
Epoch 9 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.38s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=6.35038 loss_box_reg=10.59593 loss_objectness=0.96503 loss_rpn_box_reg=0.78892 total loss=18.70026 valid: loss_classifier=1.43647 loss_box_reg=2.65499 loss_objectness=0.20781 loss_rpn_box_reg=0.23182 total loss=4.53110
Epoch 10 of 10:
train: 100%|██████████| 62/62 [01:27<00:00, 1.41s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.49it/s]
train: loss_classifier=6.45727 loss_box_reg=10.62900 loss_objectness=1.02780 loss_rpn_box_reg=0.77268 total loss=18.88675 valid: loss_classifier=1.42200 loss_box_reg=2.68870 loss_objectness=0.22488 loss_rpn_box_reg=0.22538 total loss=4.56096
Fold 3 of 5
Epoch 1 of 10:
train: 100%|██████████| 62/62 [01:29<00:00, 1.44s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.43it/s]
train: loss_classifier=6.69834 loss_box_reg=11.15031 loss_objectness=1.39912 loss_rpn_box_reg=0.87288 total loss=20.12066 valid: loss_classifier=1.37565 loss_box_reg=2.56123 loss_objectness=0.20276 loss_rpn_box_reg=0.19650 total loss=4.33613
Model saved. Loss < PrevLoss (4.33613 < 4.43079) Epoch 2 of 10:
train: 100%|██████████| 62/62 [01:33<00:00, 1.50s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.40it/s]
train: loss_classifier=6.42741 loss_box_reg=10.98338 loss_objectness=1.22858 loss_rpn_box_reg=0.90430 total loss=19.54366 valid: loss_classifier=1.33542 loss_box_reg=2.56846 loss_objectness=0.17341 loss_rpn_box_reg=0.19324 total loss=4.27053 Model saved. Loss < PrevLoss (4.27053 < 4.33613) Epoch 3 of 10:
train: 100%|██████████| 62/62 [01:32<00:00, 1.49s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.37it/s]
train: loss_classifier=6.32383 loss_box_reg=10.88289 loss_objectness=0.98719 loss_rpn_box_reg=0.79879 total loss=18.99270 valid: loss_classifier=1.32933 loss_box_reg=2.50644 loss_objectness=0.15237 loss_rpn_box_reg=0.20733 total loss=4.19548
Model saved. Loss < PrevLoss (4.19548 < 4.27053) Epoch 4 of 10:
train: 100%|██████████| 62/62 [01:29<00:00, 1.45s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.52it/s]
train: loss_classifier=6.34063 loss_box_reg=10.62006 loss_objectness=1.07334 loss_rpn_box_reg=0.77243 total loss=18.80646 valid: loss_classifier=1.37583 loss_box_reg=2.84415 loss_objectness=0.17223 loss_rpn_box_reg=0.20831 total loss=4.60051
Epoch 5 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.38s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.52it/s]
train: loss_classifier=6.05591 loss_box_reg=10.10331 loss_objectness=0.92803 loss_rpn_box_reg=0.77629 total loss=17.86355 valid: loss_classifier=1.34031 loss_box_reg=2.54433 loss_objectness=0.14679 loss_rpn_box_reg=0.20209 total loss=4.23352
Epoch 6 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=5.62321 loss_box_reg=9.80886 loss_objectness=1.06311 loss_rpn_box_reg=0.73116 total loss=17.22634 valid: loss_classifier=1.24273 loss_box_reg=2.41511 loss_objectness=0.16246 loss_rpn_box_reg=0.18229 total loss=4.00259
Model saved. Loss < PrevLoss (4.00259 < 4.19548) Epoch 7 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=5.82057 loss_box_reg=10.12465 loss_objectness=0.87938 loss_rpn_box_reg=0.68979 total loss=17.51440 valid: loss_classifier=1.29618 loss_box_reg=2.38081 loss_objectness=0.14666 loss_rpn_box_reg=0.17913 total loss=4.00279
Epoch 8 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=5.62802 loss_box_reg=9.78937 loss_objectness=0.81714 loss_rpn_box_reg=0.76712 total loss=17.00164 valid: loss_classifier=1.28435 loss_box_reg=2.51994 loss_objectness=0.15858 loss_rpn_box_reg=0.18731 total loss=4.15018
Epoch 9 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.56it/s]
train: loss_classifier=5.66886 loss_box_reg=9.95503 loss_objectness=0.79115 loss_rpn_box_reg=0.69015 total loss=17.10518 valid: loss_classifier=1.30111 loss_box_reg=2.51711 loss_objectness=0.13948 loss_rpn_box_reg=0.17974 total loss=4.13743
Epoch 10 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.55it/s]
train: loss_classifier=5.70497 loss_box_reg=10.01556 loss_objectness=0.90287 loss_rpn_box_reg=0.76567 total loss=17.38907
valid: loss_classifier=1.28378 loss_box_reg=2.44446 loss_objectness=0.15596 loss_rpn_box_reg=0.17832 total loss=4.06251
Fold 4 of 5
Epoch 1 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.49it/s]
train: loss_classifier=6.39133 loss_box_reg=11.09297 loss_objectness=1.10873 loss_rpn_box_reg=0.84009 total loss=19.43312 valid: loss_classifier=1.07224 loss_box_reg=2.65417 loss_objectness=0.13187 loss_rpn_box_reg=0.11918 total loss=3.97746
Model saved. Loss < PrevLoss (3.97746 < 4.00259) Epoch 2 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.46it/s]
train: loss_classifier=6.47518 loss_box_reg=11.11087 loss_objectness=1.48166 loss_rpn_box_reg=0.92772 total loss=19.99543 valid: loss_classifier=1.03129 loss_box_reg=2.22895 loss_objectness=0.13733 loss_rpn_box_reg=0.12639 total loss=3.52397
Model saved. Loss < PrevLoss (3.52397 < 3.97746) Epoch 3 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.49it/s]
train: loss_classifier=6.33808 loss_box_reg=11.11266 loss_objectness=0.96915 loss_rpn_box_reg=0.89252 total loss=19.31241 valid: loss_classifier=1.15412 loss_box_reg=2.27140 loss_objectness=0.09261 loss_rpn_box_reg=0.13705 total loss=3.65518
Epoch 4 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.34s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.50it/s]
train: loss_classifier=6.17701 loss_box_reg=10.82996 loss_objectness=1.01013 loss_rpn_box_reg=0.79934 total loss=18.81645 valid: loss_classifier=0.98996 loss_box_reg=2.01635 loss_objectness=0.13989 loss_rpn_box_reg=0.11231 total loss=3.25850
Model saved. Loss < PrevLoss (3.25850 < 3.52397) Epoch 5 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.34s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.47it/s]
train: loss_classifier=6.23875 loss_box_reg=10.89556 loss_objectness=1.02321 loss_rpn_box_reg=0.83733 total loss=18.99484 valid: loss_classifier=1.05185 loss_box_reg=2.12044 loss_objectness=0.13678 loss_rpn_box_reg=0.11893 total loss=3.42800
Epoch 6 of 10:
train: 100%|██████████| 62/62 [01:22<00:00, 1.33s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.50it/s]
train: loss_classifier=5.75703 loss_box_reg=10.48643 loss_objectness=1.23306 loss_rpn_box_reg=0.73452 total loss=18.21105 valid: loss_classifier=0.98932 loss_box_reg=1.96147 loss_objectness=0.12318 loss_rpn_box_reg=0.10620 total loss=3.18018
Model saved. Loss < PrevLoss (3.18018 < 3.25850) Epoch 7 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:11<00:00, 1.44it/s]
train: loss_classifier=5.78554 loss_box_reg=10.29313 loss_objectness=1.20224 loss_rpn_box_reg=0.98749 total loss=18.26841 valid: loss_classifier=0.98927 loss_box_reg=1.93243 loss_objectness=0.11978 loss_rpn_box_reg=0.10329 total loss=3.14476
Model saved. Loss < PrevLoss (3.14476 < 3.18018) Epoch 8 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.34s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.49it/s]
train: loss_classifier=5.66291 loss_box_reg=10.25977 loss_objectness=1.07119 loss_rpn_box_reg=0.81609 total loss=17.80997 valid: loss_classifier=0.99118 loss_box_reg=1.93487 loss_objectness=0.10860 loss_rpn_box_reg=0.10358 total loss=3.13822
Model saved. Loss < PrevLoss (3.13822 < 3.14476) Epoch 9 of 10:
train: 100%|██████████| 62/62 [01:22<00:00, 1.33s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.48it/s]
train: loss_classifier=5.60259 loss_box_reg=9.93354 loss_objectness=0.77883 loss_rpn_box_reg=0.68785 total loss=17.00280 valid: loss_classifier=0.95964 loss_box_reg=1.95932 loss_objectness=0.10570 loss_rpn_box_reg=0.10814 total loss=3.13280
Model saved. Loss < PrevLoss (3.13280 < 3.13822) Epoch 10 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.34s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.50it/s]
train: loss_classifier=5.69221 loss_box_reg=9.92968 loss_objectness=0.89389 loss_rpn_box_reg=0.65015 total loss=17.16593 valid: loss_classifier=0.98667 loss_box_reg=1.92044 loss_objectness=0.09116 loss_rpn_box_reg=0.10343 total loss=3.10169
Model saved. Loss < PrevLoss (3.10169 < 3.13280)
Fold 5 of 5
Epoch 1 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.37s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.57it/s]
train: loss_classifier=5.97608 loss_box_reg=10.82112 loss_objectness=1.02726 loss_rpn_box_reg=0.82257 total loss=18.64702 valid: loss_classifier=1.08137 loss_box_reg=1.74907 loss_objectness=0.16748 loss_rpn_box_reg=0.09167 total loss=3.08960
Model saved. Loss < PrevLoss (3.08960 < 3.10169) Epoch 2 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.53it/s]
train: loss_classifier=6.35777 loss_box_reg=10.92067 loss_objectness=0.99061 loss_rpn_box_reg=0.90222 total loss=19.17126 valid: loss_classifier=0.95701 loss_box_reg=1.71436 loss_objectness=0.10268 loss_rpn_box_reg=0.07398 total loss=2.84803
Model saved. Loss < PrevLoss (2.84803 < 3.08960) Epoch 3 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.56it/s]
train: loss_classifier=5.98892 loss_box_reg=10.83520 loss_objectness=1.20818 loss_rpn_box_reg=0.88810 total loss=18.92041 valid: loss_classifier=0.89238 loss_box_reg=1.81161 loss_objectness=0.08115 loss_rpn_box_reg=0.09210 total loss=2.87724
Epoch 4 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.38s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.49it/s]
train: loss_classifier=6.03716 loss_box_reg=11.06725 loss_objectness=1.01429 loss_rpn_box_reg=1.02976 total loss=19.14846 valid: loss_classifier=0.87820 loss_box_reg=1.92372 loss_objectness=0.05947 loss_rpn_box_reg=0.08287 total loss=2.94426
Epoch 5 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.38s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.52it/s]
train: loss_classifier=5.98684 loss_box_reg=10.94890 loss_objectness=1.25272 loss_rpn_box_reg=0.99512 total loss=19.18358 valid: loss_classifier=0.96853 loss_box_reg=1.69102 loss_objectness=0.09189 loss_rpn_box_reg=0.08462 total loss=2.83606
Model saved. Loss < PrevLoss (2.83606 < 2.84803) Epoch 6 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.53it/s]
train: loss_classifier=5.48019 loss_box_reg=9.95662 loss_objectness=0.95113 loss_rpn_box_reg=0.90205 total loss=17.28998 valid: loss_classifier=0.87184 loss_box_reg=1.60806 loss_objectness=0.07434 loss_rpn_box_reg=0.07817 total loss=2.63241
Model saved. Loss < PrevLoss (2.63241 < 2.83606) Epoch 7 of 10:
train: 100%|██████████| 62/62 [01:23<00:00, 1.35s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.48it/s]
train: loss_classifier=5.08530 loss_box_reg=9.82164 loss_objectness=0.75620 loss_rpn_box_reg=0.83690 total loss=16.50004 valid: loss_classifier=0.84443 loss_box_reg=1.61856 loss_objectness=0.06312 loss_rpn_box_reg=0.07750 total loss=2.60361
Model saved. Loss < PrevLoss (2.60361 < 2.63241) Epoch 8 of 10:
train: 100%|██████████| 62/62 [01:25<00:00, 1.38s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=5.30277 loss_box_reg=9.94131 loss_objectness=0.87286 loss_rpn_box_reg=0.82717 total loss=16.94411 valid: loss_classifier=0.82433 loss_box_reg=1.67320 loss_objectness=0.08087 loss_rpn_box_reg=0.07779 total loss=2.65619
Epoch 9 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.36s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.52it/s]
train: loss_classifier=5.11297 loss_box_reg=9.89085 loss_objectness=0.81817 loss_rpn_box_reg=0.76361 total loss=16.58560 valid: loss_classifier=0.82355 loss_box_reg=1.63983 loss_objectness=0.06229 loss_rpn_box_reg=0.07657 total loss=2.60223
Model saved. Loss < PrevLoss (2.60223 < 2.60361) Epoch 10 of 10:
train: 100%|██████████| 62/62 [01:24<00:00, 1.37s/it] valid: 100%|██████████| 16/16 [00:10<00:00, 1.51it/s]
train: loss_classifier=5.33651 loss_box_reg=9.71121 loss_objectness=0.96689 loss_rpn_box_reg=0.87836 total loss=16.89296 valid: loss_classifier=0.81722 loss_box_reg=1.55903 loss_objectness=0.07469 loss_rpn_box_reg=0.07303 total loss=2.52397
Model saved. Loss < PrevLoss (2.52397 < 2.60223)
# run_path = os.path.join(sys.path[0], 'runs', '05-11-21_18:17')
# print(run_path)
# model_path = os.path.join(run_path, 'models', 'faster_rcnn_model.pth')
# model = get_model(4)
# model.load_state_dict(torch.load(model_path))
# map_writer = SummaryWriter('{}/logs/mAP'.format(run_path))
testset = FaceMaskDataset(x_test, y_test, CFG.imgs_path, CFG.msks_path, CFG.img_width, CFG.img_height, transforms=get_transformer('test'))
test_loader = DataLoader(dataset=testset, batch_size=CFG.batch_size, shuffle=False, num_workers=CFG.num_workers, collate_fn=collate_fn)
def plot_test_examples(model, loader, nms_threshold):
device = CFG.device
model = model.to(device).eval()
imgs, annts = next(iter(loader))
imgs = list(img.to(device) for img in imgs)
output = model(imgs)
amount_samples = min(12, len(imgs)) # number of examples will be at most 12
for i, (img, ant, pred) in enumerate(zip(imgs, annts, output)):
if i >= amount_samples:
break
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14, 60))
axes[0].set_title('Ground Truth')
axes[1].set_title('Prediction')
axes[0].axis('off')
axes[1].axis('off')
annot_img = get_annotated_img(img.cpu().detach().numpy(), ant)
axes[0].imshow(norm(annot_img))
pred_thrs = get_pred_by_threshold(pred, nms_threshold)
pred_cpu = {k: v.cpu().detach().numpy() for k, v in pred_thrs.items()}
annot_img = get_annotated_img(img.cpu().detach().numpy(), pred_cpu, is_pred=True)
axes[1].imshow(norm(annot_img))
fig.tight_layout()
plt.show()
del imgs, pred, pred_thrs, pred_cpu
torch.cuda.empty_cache()
plot_test_examples(model, test_loader, nms_threshold=0.2)
mAP@[0.5:0.05:0.95]
def plot_precision_recall(df, iou_thresh, save_path=None):
auc_dict = dict()
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(25, 7))
plt.title('IOU Threshold {:.3f}'.format(iou_thresh))
for idx, c in enumerate(df['real_class'].unique()):
prec = df[df['real_class']==c]['precision']
recall = df[df['real_class']==c]['recall']
auc = metrics.auc(x=recall, y=prec)
auc_dict[c] = auc
axes[idx].plot(recall, prec)
axes[idx].set_title('Class {} AP={:.5f}'.format(faceMasksData.classes[c], auc))
axes[idx].grid()
axes[idx].set_ylabel('Precision')
axes[idx].set_xlabel('Recall')
fig.tight_layout()
plt.show()
if save_path:
fig.savefig('{}/AP@{:.3}.png'.format(save_path, iou_thresh))
return auc_dict
def plot_mAP_graph(iou_dict, save_path=None, iou_start=0.5, iou_step=0.05, iou_end=0.95):
x, y = zip(*sorted(iou_dict.items()))
fig = plt.figure(figsize=(25, 7))
plt.title('mAP Vs. IoU Threshold')
plt.plot(x, y, marker='o')
plt.tight_layout()
plt.grid()
plt.show()
save_path = '{}/mAP@[{:.3}:{:.3}:{:.3}].png'.format(save_path, iou_start, iou_step, iou_end)
fig.savefig(save_path)
def evaluate(model, loader, nms_thresh, iou_start=0.5, iou_step=0.05, iou_end=0.95, eps=1e-6):
metrics_path = '{}/metrics'.format(run_path)
df = get_iou_as_df(model, loader, nms_thresh)
auc_dict = dict()
for iou_trsh in np.arange(iou_start, iou_end + iou_step, iou_step):
df = calc_precision_recall(df, iou_trsh, metrics_path)
auc_dict[iou_trsh] = plot_precision_recall(df, iou_trsh, metrics_path)
iou_dict = calc_mAP_from_auc_dict(auc_dict)
classes_mAP = calc_mAP_per_class(auc_dict, iou_dict)
plot_mAP_graph(iou_dict, metrics_path, iou_start, iou_step, iou_end)
total_mAP = .0
for iou in iou_dict:
print('mAP@{:.3} =\t{:.5}'.format(iou, iou_dict[iou]))
print('\nmAP Per Class:')
for c, val in classes_mAP.items():
total_mAP += val
print('{}\t\tmAP@[0.5:0.05:0.95] =\t{:.5f}'.format(faceMasksData.classes[c], classes_mAP[c]))
total_mAP /= len(classes_mAP)
print('Total mAP@[0.5:0.05:0.95] =\t{:.5f}'.format(total_mAP))
evaluate(model, test_loader, CFG.nms_thresh)
evaluating IoU: 100%|██████████| 9/9 [00:08<00:00, 1.09it/s]
mAP@0.5 = 0.68686 mAP@0.55 = 0.68355 mAP@0.6 = 0.68257 mAP@0.65 = 0.67294 mAP@0.7 = 0.66716 mAP@0.75 = 0.65268 mAP@0.8 = 0.54443 mAP@0.85 = 0.41741 mAP@0.9 = 0.22502 mAP@0.95 = 0.02037 mAP Per Class: with_mask mAP@[0.5:0.05:0.95] = 0.76307 without_mask mAP@[0.5:0.05:0.95] = 0.71104 mask_weared_incorrect mAP@[0.5:0.05:0.95] = 0.10179 Total mAP@[0.5:0.05:0.95] = 0.52530